/*
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package io.iec.edp.caf.data.orm.jpa;

import io.iec.edp.caf.commons.utils.InvokeService;
import io.iec.edp.caf.commons.utils.SpringBeanUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.transaction.support.TransactionSynchronizationAdapter;
import org.springframework.transaction.support.TransactionSynchronizationManager;

import java.util.Map;

/**
 * @author wangyandong
 * @date 2019/9/6 9:48
 *
 */
@Slf4j
public class DynamicTableContext implements AutoCloseable {
    private final static ThreadLocal<DynamticTableInfo> threadLocal = new ThreadLocal<>();

    private boolean flushable;

    /**
     * 私有构造函数，防止开发人员直接创建
     */
    private DynamicTableContext(Map<String,String> tableNameMappings){
        this.flushable = true;
        DynamticTableInfo info = threadLocal.get();
        if(info==null){
            info = new DynamticTableInfo();
            info.setDynamicTableNames(tableNameMappings);
        }else{
            info.getDynamicTableNames().putAll(tableNameMappings);
        }
        if(log.isInfoEnabled()){
            String stringInfo = "";
            for(Map.Entry<String,String> entry:tableNameMappings.entrySet()){
                String srcTableName = entry.getKey();
                String destTableName = entry.getValue();
                stringInfo = stringInfo +"Change "+srcTableName+" to "+destTableName;
            }
            log.info("已创建动态表上下文，上下文信息："+stringInfo+", 编号："+Thread.currentThread().getId());
        }
        threadLocal.set(info);

    }

    /**
     * 私有构造函数，防止开发人员直接创建
     */
    private DynamicTableContext(){
        this.flushable = false;
    }

    /**
     * 用于年度表、动态表场景创建上下文
     * @param tableNameMappings
     * @return
     */
    public static DynamicTableContext createContext(Map<String,String> tableNameMappings){
        return new DynamicTableContext(tableNameMappings);
    }

    /**
     * 内部调用，外面无需访问
     * @return
     */
    public static DynamicTableContext getContext(){
        return new DynamicTableContext();
    }

    /**
     * 获取动态表信息
     * @return
     */
    public DynamticTableInfo getDynamicTableInfo() {
        return threadLocal.get();
    }

    /**
     * 关闭时清理上下文信息
     * @throws Exception
     */
    @Override
    public void close() throws Exception{
        if(flushable && isInTransaction() == false) {
            if(log.isInfoEnabled())
                log.info("不存在事务，当前线程动态表已清除。编号：" + Thread.currentThread().getId());
            clearContext();
        }
    }

    /**
     * 强制清理上下文
     * @throws Exception
     */
    public static void clearContext() throws Exception{
        //todo 是否应该用remove方法防止内存泄漏？
        threadLocal.set(null);
    }

    private boolean isInTransaction(){
        //1、得到事务管理对象和事务对象：
//        JpaTransactionManager manager = SpringBeanUtils.getBean(JpaTransactionManager.class);
//        if(manager == null)
//            return false;
//        Object transaction = InvokeService.invokeProtectedMethod(manager,"doGetTransaction");
//        if(transaction ==null)
//            return false;

        //boolean inTransaction = (Boolean) InvokeService.invokeMethod(transaction,"hasTransaction");
        boolean inTransaction = TransactionSynchronizationManager.isActualTransactionActive();
        if(inTransaction){
            if(log.isInfoEnabled())
                log.info("存在事务，待事务提交后清除当前动态表。编号："+Thread.currentThread().getId());
            TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
                                                                          @Override
                                                                          public void afterCommit() {
                                                                              log.info("事务已提交，当前线程动态表清除。编号："+Thread.currentThread().getId());
                                                                              threadLocal.set(null);
                                                                          }
                                                                      }
            );
        }
        return inTransaction;
    }
}
